#if !PocketPC
using System;
using System.IO;
using System.Collections;
using System.Text;
using System.Security.Permissions;

namespace GeoFramework.IO.Usb
{
    [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
	public abstract class UsbDevice : Device 
	{
        private UsbDeviceClass pClass;
        private string pDevicePath;
        private string pDeviceName;
        private int pIndex;
        //private int pDeviceInstance;
        private SP_DEVINFO_DATA pDeviceInformation;
        private UsbDeviceCapabilities pCapabilities;
        private ArrayList pRemovableDevices;
        //private UsbDevice pParent;

        protected UsbDevice(UsbDeviceClass deviceClass, string devicePath, int deviceInstance, int reserved, int index)
        {
            pClass = deviceClass;

            if (devicePath != null)
                pDevicePath = devicePath.Replace(@"\\?\", @"\\.\");

            pDeviceInformation = new SP_DEVINFO_DATA();
            pDeviceInformation.classGuid = pClass.ClassGuid;
            pDeviceInformation.devInst = deviceInstance;
            pDeviceInformation.reserved = reserved;

            pIndex = index;

            // Load up properties for this USB device
            pDeviceName = pClass.GetProperty(pDeviceInformation, NativeMethods.SPDRP_FRIENDLYNAME, null);
            pCapabilities = (UsbDeviceCapabilities)pClass.GetProperty(pDeviceInformation, NativeMethods.SPDRP_CAPABILITIES, 0);

            //// Locate this device's parent (if any)
            //int parentDevInst = 0;
            //int hr = NativeMethods.CM_Get_Parent(ref parentDevInst, pDeviceInformation.devInst, 0);
            //if (hr == 0)
            //{
            //    pParent = new UsbDevice(pClass, null, pClass.GetInfo(parentDevInst), -1);
            //}

            // Load up the list of removable devices
            pRemovableDevices = new ArrayList();
            if (IsRemovable)
            {
                pRemovableDevices.Add(this);
            }
//            else
//            {
//                if (pParent != null)
//                {
//                    foreach (Device device in pParent.RemovableDevices)
//                    {
//                        pRemovableDevices.Add(device);
//                    }
//                }
//            }
        }

        /// <summary>
        /// Returns the index of the device amongst a list of USB devices.
        /// </summary>
        public int Index
        {
            get
            {
                return pIndex;
            }
        }
       
        /// <summary>
        /// Returns the USB device class to which this device belongs.
        /// </summary>
        public UsbDeviceClass Class
        {
            get
            {
                return pClass;
            }
        }

        /// <summary>
        /// Returns the full path to this USB device.
        /// </summary>
        public string Path
        {
            get
            {
                return pDevicePath;
            }
        }

        /// <summary>
        /// Returns the instance handle for the USB device.
        /// </summary>
        public int InstanceHandle
        {
            get
            {
                return pDeviceInformation.devInst;
            }
        }

        /// <summary>
        /// Returns a friendly name for the USB  device.
        /// </summary>
        public string DeviceName
        {
            get
            {
                return pDeviceName;
            }
        }


        /// <summary>
        /// Returns a new <strong>SerialStream</strong> for communicating with the
        /// device.
        /// </summary>
        public override Stream GetHardwareStream()
        {
            if (IsImmediateNeedStreamAvailable)
            {
                IsImmediateNeedStreamAvailable = false;
                return ImmediateNeedStream;
            }
            else
            {
                throw new IOException("The GetHardwareStream() method of the UsbDevice class cannot be called directly.  It is the responsibility of classes which inherit from UsbStream to provide a Stream since stream types vary for each USB device.");
            }
        }

        /// <summary>
        /// Indicates whether the device can be locked for exclusive use.
        /// </summary>
        public bool IsLockSupported
        {
            get
            {
                return (pCapabilities & UsbDeviceCapabilities.IsLockSupported) != 0;
            }
        }

        /// <summary>
        /// Indicates whether the device supports removable media such as a CD-ROM or DVD.
        /// </summary>
        public bool HasRemovableMedia
        {
            get
            {
                return (pCapabilities & UsbDeviceCapabilities.HasRemovableMedia) != 0;
            }
        }

        /// <summary>
        /// Returns whether the entire device can be removed from the system.
        /// </summary>
        public bool IsRemovable
        {
            get
            {
                return (pCapabilities & UsbDeviceCapabilities.HasRemovableMedia) != 0;
            }
        }

        /// <summary>
        /// Returns whether the device's USB cable can be unplugged safely without any problems or data loss.
        /// </summary>
        public bool IsSuddenRemovalAllowed
        {
            get
            {
                return (pCapabilities & UsbDeviceCapabilities.IsSuddenRemovalAllowed) != 0;
            }
        }

        /// <summary>
        /// Returns whether the device is currently enabled on the system.
        /// </summary>
        /// <remarks>This property will return <strong>True</strong> if the USB device has been disabled in the Control Panel.</remarks>
        public bool IsEnabled
        {
            get
            {
                return (pCapabilities & UsbDeviceCapabilities.IsHardwareDisabled) == 0;
            }
        }

        /// <summary>
        /// Returns whether the device's drivers are installed without any notification to the user.
        /// </summary>
        /// <remarks>Silent installation is typically performed on low-level devices such as motherboards and
        /// processors, where no user interation is required, or notification to the user would only be confusing.  This
        /// flag is always disabled for any devices not critical to the operation of the computer.</remarks>
        public bool IsInstalledSilently
        {
            get
            {
                return (pCapabilities & UsbDeviceCapabilities.IsInstalledSilently) != 0;
            }
        }

        /// <summary>
        /// Gets this device's list of removable devices.
        /// Removable devices are parent devices that can be removed.
        /// </summary>
        public ArrayList RemovableDevices
        {
            get
            {
                return pRemovableDevices;
            }
        }

        ///// <summary>
        ///// Returns the USB device which is hosting this USB device.
        ///// </summary>
        //public UsbDevice Parent
        //{
        //    get
        //    {
        //        return pParent;
        //    }
        //}

        public override string ToString()
        {
            return DeviceName;
        }
    }

    /// <summary>
    /// Indicates the supported features and current status of a USB device.
    /// </summary>
    [Flags]
    public enum UsbDeviceCapabilities
    {
        /// <summary>
        /// The features of the USB device are unknown or not yet determined.
        /// </summary>
        Unknown = 0x00000000,
        /// <summary>
        /// The device can be locked for exclusive access.
        /// </summary>
        IsLockSupported = 0x00000001,
        /// <summary>
        /// The device has removable media which can be ejected from the system.
        /// </summary>
        HasRemovableMedia = 0x00000002,
        /// <summary>
        /// The entire device is removable from the system.
        /// </summary>
        IsRemovable = 0x00000004,
        /// <summary>
        /// The device is docked to the current system.
        /// </summary>
        IsDocked = 0x00000008,
        /// <summary>
        /// The device contains a unique identifier separating itself from other devices.
        /// </summary>
        HasUniqueIdentifier = 0x00000010,
        /// <summary>
        /// The device is installed without any interaction with the user.
        /// </summary>
        IsInstalledSilently = 0x00000020,
        /// <summary>
        /// The device supports raw communication modes not otherwise allowed.
        /// </summary>
        IsRawDevice = 0x00000040,
        /// <summary>
        /// The device's USB cable can be safely, suddenly unplugged.
        /// </summary>
        IsSuddenRemovalAllowed = 0x00000080,
        /// <summary>
        /// The device is currently disabled.
        /// </summary>
        IsHardwareDisabled = 0x00000100,
        NonDynamic = 0x00000200,
    }
}
#endif